/**
 * 完全模拟 vue composition API 和 setup 函数
 * useLocalStore(from mobx-react) 提供如vue一般的可响应数据源
 * setup 可以调用多个 useHook 组合，然后输出导入到组件的对象
 * FC 使用 useVueSetup(setupFn(props)); 导入 setup 结果，同时可以在 dev-tool 看到导入的全部内容
 * FC 内部使用 <Observer>{()=>renderFn}</Observer> 注册变更重刷，还可以内部细粒度的包裹，减少不必要的重渲染范围的扩大
 */

import React, { useState, useEffect } from 'react';
import { reaction, autorun } from 'mobx';
import { Observer, useLocalStore } from 'mobx-react';
import styles from './try_like_vue3.css';

interface ITodo {
  id: string;
  text: string;
  completed: boolean;
}

// 模拟参与组合的第一个 vue setup useHook
function useTodos() {
  const todoStore = useLocalStore(() => ({
    todos: [] as ITodo[], // state
    addTodo(v: ITodo) {
      // 相当于 redux dispatch actions
      todoStore.todos.push(v);
    },
    addTodoRandom() {
      todoStore.todos.push({
        id: String(Date.now()),
        text: String(Math.random()),
        completed: Math.random() < 0.5,
      } as ITodo);
    },
    toggleTodoThunk(todo: ITodo) {
      return () => {
        todo.completed = !todo.completed;
      };
    },
  }));
  return todoStore;
}

// 模拟参与组合的第二个 vue setup useHook
function useColor() {
  const store = useLocalStore(() => ({
    color: 'red',
  }));
  const setBlue = () => {
    store.color = 'blue';
  };
  const setGreen = () => {
    store.color = 'green';
  };
  return { store, setBlue, setGreen };
}

/**
 * 仅仅为了在 dev-tool 中能看到是 vue setup，方便查看最终导出的内容
 * @param init 初始 setup 执行结果
 */
function useVueSetup(init: any) {
  return useState(init);
}

// 模拟 vue setup 函数, 把2个hook集成后导出供 view 引用
function vueSetup(id: string) {
  const color = useColor();
  const todoStore = useTodos();
  // 所有设置，只在 useEffect 设置一次即可
  useEffect(() => {
    // 响应数据变化
    autorun(() => {
      console.log('color changed to', color.store.color);
    });
    // 这里演示自定义hook间的联动
    reaction(
      () => color.store.color,
      color => {
        return;
        todoStore.addTodo({
          id: String(Date.now()),
          text: color + ' todo',
          completed: false,
        });
      },
    );
  }, [color.store.color, todoStore]);
  return { color, todoStore };
}

const set = new Set();

function Vue3Component() {
  const [{ color, todoStore }] = useVueSetup(vueSetup('init'));
  console.log('parent re-renderd');

  // 验证 useLocalStore 里面的方法是否都是稳定的，测试结果是稳定的
  set.add(todoStore.toggleTodoThunk);
  console.log('set.size', set.size);

  // <Observer> 中必须是 lazy 的 render child prop，以便收集信息
  // 内部使用 <Observer> 的好处就是外部组件几乎不用重新渲染，一次性准备好就行了
  // 再往里面使用 Observer 可以控制变化只触发更小范围的重新刷新，如下面 color/todos 互相不干扰

  return (
    <Observer>
      {() => {
        console.log('parent-in re-renderd');
        return (
          <div className={styles.normal}>
            <Observer>
              {() => {
                console.log('render color');
                return (
                  <>
                    <span style={{ color: color.store.color }}>
                      this color is {color.store.color}
                    </span>
                    <button onClick={() => color.setBlue()}>setBlue</button>
                    <button onClick={() => color.setGreen()}>setGreen</button>
                    <button onClick={() => todoStore.addTodoRandom()}>
                      ADD TODO
                    </button>
                  </>
                );
              }}
            </Observer>

            <ol>
              {todoStore.todos.map((todo: ITodo) => (
                <Observer key={todo.id}>
                  {() => (
                    <li onClick={todoStore.toggleTodoThunk(todo)}>
                      {todo.text}
                      {todo.completed ? '完成' : '未完成'}
                    </li>
                  )}
                </Observer>
              ))}
            </ol>
          </div>
        );
      }}
    </Observer>
  );
}

export default Vue3Component;
